#include <QDebug>
#include <QTimer>
#include <QFile>
#include <QDir>
#include <QSqlQuery>
#include <QSqlError>
#include <QRegExp>
#include <QDateTime>
#include "macro.h"
#include "webserver.h"
#include "threadofwebclientsocket.h"
#include "webclientsocket.h"
#include "dboinwebserver.h"

#define TIME_BEFORE_LISTEN 500
#define Min_HTML_SIZE 20
#define WEB_PAGE_DIR_NAME "www/GeneratedFile"
#define WEB_PAGE_DIR_TemplateNAME "www/template"
#define Template_Node_Name "node_std"

WebServer::WebServer(quint16 port,
       QString strDefaultPage,quint16 updateInterval,quint32 updateIntervalPic,QObject *parent) :
    QTcpServer(parent)
{
    webPort=port;
    defaultPage=strDefaultPage;
    updtInterval=updateInterval;
    updtIntervalPic=updateIntervalPic;

    dboInWS=new DBOInWebserver(this);
    initCompleted=false;

}

void WebServer::slotConnectDB()
{
    if(!dboInWS->createConnection(DB_NAME,DB_HOST,DB_USERNAME,
                               DB_PWD,QString(DB_CON_NAME_WEBSERVER)))
        emit sigErrorInfo("Connection "DB_CON_NAME_WEBSERVER" failed.\n"
                          +tr("数据库连接失败，程序无法正常运行。请重启程序"));
    else
        emit sigErrorInfo("connection "DB_CON_NAME_WEBSERVER" success");


    timerForUpdateWebSensor=new QTimer();
    timerForUpdateWebSensor->start(updtInterval*1000);
    connect(timerForUpdateWebSensor,SIGNAL(timeout()),this,SLOT(slotTimerEventForUpdateWebSensor()));

    timerForUpdateWebPic=new QTimer();
    timerForUpdateWebPic->start(updtIntervalPic); //不用乘以1000
    connect(timerForUpdateWebPic,SIGNAL(timeout()),this,SLOT(slotTimerEventForUpdateWebPic()));

    QTimer::singleShot(TIME_BEFORE_LISTEN,this,SLOT(slotListen()));
}

void WebServer::incomingConnection(int handle)
{
   // qDebug()<<"Incomming connection to WebServer";
    lastThreadSocket=new ThreadOfWebClientSocket(handle,defaultPage);
    lastThreadSocket->start();
}

void WebServer::slotListen()
{
    if(!listen(QHostAddress::Any,webPort))
    {
        emit sigMsgBox("ERROR","Web Server fail to listen");
    }
    initWebPages();
    initCompleted=true;
}

void WebServer::slotTimerEventForUpdateWebPic()
{
    if(!initCompleted)
        return;

    qint16 sensorID;
    qint64 nodeMacAddress;
    QByteArray nodeName,sensorName;
    QFile file;
    QSqlQuery tempQuery(QSqlDatabase::database(dboInWS->dboConnectionName));
    QSqlQuery tempQuery2(QSqlDatabase::database(dboInWS->dboConnectionName));

    if(tempQuery.exec("SELECT nsm.SensorID,nsm.NodeMacAddress,NodeName,sensorname from NodeAddressMap nam,NodeSensorMap "
                           "nsm,typemap tm where nam.nodemacaddress=nsm.nodemacaddress and nsm.sensorid=tm.sensorid and NodeOnWork=1 "
                           "and (nsm.SensorID>="+QByteArray::number(SensorTypeVideoMin)+" and nsm.SensorID<="+QByteArray::number(SensorTypeVideoMax)+")"))
    {
        while(tempQuery.next())
        {            
            sensorID=tempQuery.value(0).toInt();
            nodeMacAddress=tempQuery.value(1).toLongLong();
            nodeName=tempQuery.value(2).toByteArray();
            if(nodeName.isEmpty())
                continue;

            sensorName=tempQuery.value(3).toByteArray();

            QDir dirTemp(WEB_PAGE_DIR_NAME"/"+nodeName);
            if(!dirTemp.exists()) //该节点没有对应的文件夹，则建文件夹
            {
                slotAddNode(nodeMacAddress,nodeName);
            }

            file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");

            QByteArray fileContent;

            QByteArray contentToBeIndexed="<div id=\"Current Value\">";
            qint16 offSet;
            if(sensorID>=SensorTypeVideoMin&&sensorID<=SensorTypeVideoMax)
            {
                QByteArray sensorData;
                QDateTime dateTime;

                QString tableName="multi"+QDate::currentDate().toString("yyyy_MM_dd")+"_mac"+
                        QString::number(nodeMacAddress);
                if(tempQuery2.exec("select datetime,sensordata,now() from "+tableName+
                                  " where id>(select max(id) from "+tableName+")-25 and id>1 and "
                                  "sensorid="+QByteArray::number(sensorID,10)+" order by id desc limit 1"))
                {
                    if(tempQuery2.next())
                    {
                        QFile fileTemp(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");

                        dateTime=tempQuery2.value(0).toDateTime();
                        QDateTime currentServerTime=tempQuery2.value(2).toDateTime();
                        qint32 secsToCurrent=dateTime.secsTo(currentServerTime);
                        QByteArray da=tempQuery2.value(1).toByteArray();
                        if(secsToCurrent<5&&secsToCurrent>=0&&da.size()>20) //不大于5秒之前,表明视频是活动状态
                        {
                            if(!fileTemp.exists()||fileTemp.size()<Min_HTML_SIZE) //没有给该传感器建立文件，则先建立
                            {
                                slotAddSensor(nodeMacAddress,sensorID);
                                emit sigRegSensor(nodeMacAddress,sensorID,1);
                            }
                        }
                        else //大于5秒之前，传感器已经不活动
                        {
                            if(fileTemp.exists())
                            {

                            }
                            continue;
                        }

                        if(sensorName=="VideoMJPEG")
                        {
                            fileTemp.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".jpeg");
                            sensorData=tempQuery2.value(1).toByteArray();

                            if(fileTemp.open(QIODevice::ReadWrite)&&sensorData.size()>0)
                            {
                                fileTemp.resize(0);
                                fileTemp.write(sensorData);
                                fileTemp.close();
                            }

                            if(file.open(QIODevice::ReadWrite))
                                fileContent=file.readAll();
                            else
                                continue;

                            contentToBeIndexed="<div id=\"Updated Time\">";
                            offSet=fileContent.indexOf(contentToBeIndexed)+contentToBeIndexed.size();
                            qint16 offSet2=fileContent.indexOf("</div>",offSet);
                            fileContent.remove(offSet,offSet2-offSet);
                            fileContent.insert(offSet,dateTime.toString("yyyy MM/dd hh:mm:ss"));

                            file.resize(0);
                            file.write(fileContent);
                            file.close();
                         }//if(sensorName=="VideoMJPEG")结束                      
                    } //end of "if(tempQury->next())"
                }

            }//视频结束

        } //end of "while(dboInWS->qury->next())"
    } //end of "quey.exec()"
    else
    {
        qDebug()<<"Fail in slotTimerForUpdateWebPic()."<<tempQuery.lastError();
        return;
    }
}

void WebServer::slotTimerEventForUpdateWebSensor()
{
    if(!initCompleted)
        return;

    qint16 sensorID;
    qint64 nodeMacAddress;
    QByteArray nodeName,sensorName;
    QFile file;
    QSqlQuery tempQuery(QSqlDatabase::database(dboInWS->dboConnectionName));
    QSqlQuery tempQuery2(QSqlDatabase::database(dboInWS->dboConnectionName));

    if(tempQuery.exec("SELECT nsm.SensorID,nsm.NodeMacAddress,NodeName,sensorname from NodeAddressMap nam,NodeSensorMap "
                           "nsm,typemap tm where nam.nodemacaddress=nsm.nodemacaddress and nsm.sensorid=tm.sensorid and NodeOnWork=1 "
                           "and (nsm.SensorID<"+QByteArray::number(SensorTypeVideoMin)+" or nsm.SensorID>"+QByteArray::number(SensorTypeVideoMax)+")"))
    {
        while(tempQuery.next())
        {
            sensorID=tempQuery.value(0).toInt();
            nodeMacAddress=tempQuery.value(1).toLongLong();
            nodeName=tempQuery.value(2).toByteArray();
            if(nodeName.isEmpty())
                continue;

            sensorName=tempQuery.value(3).toByteArray();

            QDir dirTemp(WEB_PAGE_DIR_NAME"/"+nodeName);
            if(!dirTemp.exists()) //该节点没有对应的文件夹，则建文件夹
            {
                slotAddNode(nodeMacAddress,nodeName);
            }

            file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");

            QByteArray fileContent;

            QByteArray contentToBeIndexed="<div id=\"Current Value\">";
            qint16 offSet;

            QByteArray strSensorData;
            QDateTime dateTime;
            if(tempQuery2.exec("select datetime,sensordata,now() from rtoday where "
                              " id>(select max(id) from rtoday)-15 and NodeMacAddress="+
                  QByteArray::number(nodeMacAddress)+" and SensorID="+QByteArray::number(sensorID)+
                           " order by id desc limit 1"))
            {
                if(tempQuery2.next())
                {
                    QFile fileTemp(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");
                    dateTime=tempQuery2.value(0).toDateTime();
                    QDateTime currentServerTime=tempQuery2.value(2).toDateTime();
                    qint32 secsToCurrent=dateTime.secsTo(currentServerTime);
                    if(secsToCurrent<TTL_MAX/2) //不大于TTL_MAX/2秒之前,表明传感器是活动状态
                    {
                        if(!fileTemp.exists()||fileTemp.size()<Min_HTML_SIZE) //没有给该传感器建立文件，则先建立
                        {
                            slotAddSensor(nodeMacAddress,sensorID);
                            emit sigRegSensor(nodeMacAddress,sensorID,1);
                        }
                    }
                    else
                        continue;

                    if(file.open(QIODevice::ReadWrite))
                        fileContent=file.readAll();
                    else
                        continue;

                    offSet=fileContent.indexOf(contentToBeIndexed)+contentToBeIndexed.size();
                    qint16 offSet2;
                    offSet2=fileContent.indexOf("</div>",offSet);
                    fileContent.remove(offSet,offSet2-offSet);
                    strSensorData=tempQuery2.value(1).toByteArray();
                    fileContent.insert(offSet,strSensorData);

                   contentToBeIndexed="<div id=\"Updated Time\">";
                   offSet=fileContent.indexOf(contentToBeIndexed)+contentToBeIndexed.size();
                   offSet2=fileContent.indexOf("</div>",offSet);
                   fileContent.remove(offSet,offSet2-offSet);
                   fileContent.insert(offSet,dateTime.toString("yyyy MM/dd hh:mm:ss"));

                   file.resize(0);
                   file.write(fileContent);
                   file.close();
                }
            }
        }
    }
    else
    {
        qDebug()<<"Fail in slotTimerForUpdateWebSeensor()."<<tempQuery.lastError();
        return;
    }
}

void WebServer::slotAddNode(qint64 nodeMacAddr, QByteArray nodeName)
{
    if(nodeName.isEmpty())
        return;
    if(!initCompleted)
        return;

    QFile file;
    file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+".html");
    if(file.exists()) //已经存在该节点文件
        return;

    QDir dir(WEB_PAGE_DIR_NAME);
    file.rename(WEB_PAGE_DIR_NAME"/node_std.html",WEB_PAGE_DIR_NAME"/"+nodeName+".html");
    file.copy(WEB_PAGE_DIR_TemplateNAME"/node_std.html",WEB_PAGE_DIR_NAME"/node_std.html");
    dir.mkdir(nodeName);

    file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+".html");

    if(!file.exists()||file.size()<=Min_HTML_SIZE)
        return;
    if(file.open(QIODevice::ReadWrite))
    {
        QByteArray fileContent=file.readAll();
        QByteArray divNodeName="<div id=\"node_name\">";
        qint16 offSet=fileContent.indexOf(divNodeName);
        offSet+=divNodeName.size(); //移到下一个位置

        fileContent.insert(offSet,nodeName);
        file.resize(0);
        file.write(fileContent); //重新写文件

        file.close();
    }

    file.setFileName(WEB_PAGE_DIR_NAME"/nodelist.html");
    if(!file.exists()||file.size()<=Min_HTML_SIZE)
        return;
    if(file.open(QIODevice::ReadWrite))
    {
        QByteArray fileContent=file.readAll();
        QByteArray divNodeName="<div id=\"nodelist\">";
        qint16 offSet=fileContent.indexOf(divNodeName);
        offSet+=divNodeName.size(); //移到下一个位置
        QByteArray dataToBeIns="\n<div id=\"node name\">"+nodeName+"<a href=\""+nodeName+
                ".html\">  >Detail</a></div>";
        if(fileContent.indexOf(dataToBeIns,offSet)>0)
            return;
        fileContent.insert(offSet,dataToBeIns);
        file.resize(0);
        file.write(fileContent);

        file.close();
    }

}

void WebServer::slotDeleteNode(qint64 nodeMacAddr, QByteArray nodeName)
{
    if(nodeName.isEmpty())
        return;
    if(!initCompleted)
        return;

    QFile file;

    /*修改nodelist.html*/
    file.setFileName(WEB_PAGE_DIR_NAME"/nodelist.html");
    if(file.open(QIODevice::ReadWrite))
    {
        QByteArray fileContent=file.readAll();
        QByteArray divNodeName("<div id=\"node name\">"+nodeName+"<a href=\""+nodeName+".html\""+
                               ">  >Detail</a></div>");

        qint16 offSet=fileContent.indexOf(divNodeName)-1;//把换行符也删除
        if(offSet>0)
        {
            fileContent.remove(offSet,divNodeName.size()+1);
            file.resize(0);
            file.write(fileContent);
        }

        file.close();
    }
    /*删除nodename.html和文件夹nodename*/
    QDir dir(WEB_PAGE_DIR_NAME);
    dir.remove(nodeName+".html");
    deleteFilesInDir(WEB_PAGE_DIR_NAME"/"+nodeName);
    dir.rmdir(nodeName);
}

void WebServer::slotAddSensor(qint64 nodeMacAddr, qint16 sensorID)
{    
    if(!initCompleted)
        return;

    QByteArray sensorName,nodeName;
    QSqlQuery tempQuery(QSqlDatabase::database(dboInWS->dboConnectionName));

    if(tempQuery.exec("select SensorName from typemap where sensorID="+
                        QByteArray::number(sensorID,10)))
    {
        if(tempQuery.next())
            sensorName=tempQuery.value(0).toByteArray();
        else
            return;

        if(tempQuery.exec("select nodename from nodeaddressmap where NodeMacAddress="+
                               QByteArray::number(nodeMacAddr)))
        {
            if(tempQuery.next())
            {
                nodeName=tempQuery.value(0).toByteArray();
                if(nodeName.isEmpty())
                    return;
            }
            else
                return;
        }
        else
        {
            return;
        }
    }
    else
    {
        return;
    }

    QFile file;    

    file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");
    if(file.exists()&&file.size()>Min_HTML_SIZE) //已经有该传感器的页面,且大小正常
        return;

    file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+".html");
    if(!file.exists()||file.size()<=Min_HTML_SIZE) //不存在节点文件
    {
        slotAddNode(nodeMacAddr,nodeName);
        slotAddSensor(nodeMacAddr,sensorID);
        return;
    }

    if(file.open(QIODevice::ReadWrite))
    {
        QByteArray fileContent=file.readAll();
         QByteArray headLocation="<h3>Location</h3>";
        qint16 offSet=fileContent.indexOf(headLocation);
        offSet--; //移到上一个位置
        QByteArray dataToBeIns="\r\n<h3>"+sensorName+"</h3>"
                +"\r\n<div id=\""+sensorName+"\"><a href=\"./"+nodeName+
                "/"+sensorName+".html\">>>Detail</a></div>\r\n";
        if(!fileContent.contains(dataToBeIns))
        {
            fileContent.insert(offSet,dataToBeIns);
            file.resize(0);
            file.write(fileContent); //重新写文件
        }

        file.close();
    }

    file.copy(WEB_PAGE_DIR_TemplateNAME"/node_std/temperature.html",WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");
    file.setFileName(WEB_PAGE_DIR_NAME"/"+nodeName+"/"+sensorName+".html");
    if(!file.exists()||file.size()<=Min_HTML_SIZE)
        return;

    if(file.open(QIODevice::ReadWrite))
    {
        QByteArray fileContent=file.readAll();
         QByteArray contentToBeIndexed="node_std.html\">";
        qint16 offSet=fileContent.indexOf(contentToBeIndexed);
        fileContent.remove(offSet,contentToBeIndexed.size());
        QByteArray dataToBeIns=nodeName+".html\">";
        fileContent.insert(offSet,dataToBeIns);

        contentToBeIndexed="<h1>Temperature</h1>";
        offSet=fileContent.indexOf(contentToBeIndexed);
        fileContent.remove(offSet+4,11);
        fileContent.insert(offSet+4,sensorName);

        contentToBeIndexed="<div id=\"node name\">";
        offSet=fileContent.indexOf(contentToBeIndexed);
        offSet+=contentToBeIndexed.size();
        fileContent.remove(offSet,8);
        fileContent.insert(offSet,nodeName);

        if(sensorID>=SensorTypeVideoMin&&sensorID<=SensorTypeVideoMax) //视频
        {
            contentToBeIndexed="<div id=\"Deviation\">"; //偏差值
            offSet=fileContent.indexOf(contentToBeIndexed);
            offSet+=contentToBeIndexed.size();
            fileContent.remove(offSet,10);
            fileContent.insert(offSet,"UnDefined");

            contentToBeIndexed="<body>"; //JavaScript
            offSet=fileContent.indexOf(contentToBeIndexed);
            offSet+=contentToBeIndexed.size();
            QByteArray contToBeIns="/n<script>setTimeout(\"location.href='"+
               sensorName+".html'\","+QByteArray::number(updtIntervalPic)+");</script>\n";
            if(!fileContent.contains(contToBeIns))
                fileContent.insert(offSet,contToBeIns);
        }

        contentToBeIndexed="<div id=\"Current Value\">";
        offSet=fileContent.indexOf(contentToBeIndexed)+contentToBeIndexed.size();
        if(sensorID>=SensorTypeVideoMin&&sensorID<=SensorTypeVideoMax)
        {
            if(sensorID==SensorTypeVideoMJPEG)
            {

                fileContent.insert(offSet,"<img src=\""+sensorName+".jpeg"
                                   "\"  alt=\"just an image\"/>");
            }
            else
                fileContent.insert(offSet,"Format Not Supported Now");
        }

        contentToBeIndexed="<div id=\"unit\">";
        offSet=fileContent.indexOf(contentToBeIndexed);
        offSet+=contentToBeIndexed.size();
        fileContent.remove(offSet,5);
        if(sensorName.toUpper()=="TEMPERATURE")
            dataToBeIns="&degC";
        else if(sensorName.toUpper()=="HUMIDITY")
            dataToBeIns="%";
        else if(sensorName.toUpper()=="VOLTAGE")
            dataToBeIns="V";
        else if(sensorName.toUpper()=="LIGHT")
            dataToBeIns="Lux";
        else
            dataToBeIns="Undefined";
        fileContent.insert(offSet,dataToBeIns);

        contentToBeIndexed="<div id=\"sampleinterval\">";
        offSet=fileContent.indexOf(contentToBeIndexed);
        offSet+=contentToBeIndexed.size();
        if(sensorID<SensorTypeVideoMin||sensorID>SensorTypeVideoMax)
            dataToBeIns=QByteArray::number(updtInterval);
        else
            dataToBeIns=QByteArray::number(updtIntervalPic/1000.0);

        fileContent.insert(offSet,dataToBeIns);

        file.resize(0);
        file.write(fileContent); //重新写文件
        file.close();
    }

}

void WebServer::initWebPages()
{ 
    deleteFilesInDir(WEB_PAGE_DIR_NAME);
    copyDir(WEB_PAGE_DIR_NAME,WEB_PAGE_DIR_TemplateNAME);
}

//把strDir目录下的内容全部删除
void WebServer::deleteFilesInDir(QString strDir)
{
    QDir dir(strDir);

    while(1)
    {
        QStringList listFileName=dir.entryList(QDir::Files);
        while(!listFileName.isEmpty())
        {
            dir.remove(listFileName.at(0));
            listFileName.removeFirst();
        }
        QStringList listDir=dir.entryList(QDir::Dirs);

        if(listDir.size()<=2)
            break; //只有上级目录和当前目录
        else
        {
            while(listDir.size()>0)
            {
                if(listDir.at(0)=="."||listDir.at(0)=="..")
                {
                    listDir.removeFirst();
                }
                else
                {
                    deleteFilesInDir(strDir+"/"+listDir.at(0));
                    dir.rmdir(dir.absolutePath()+"/"+listDir.at(0));
                    listDir.removeFirst();
                }
            }
        }
    }
}

//把oldDir目录下的文件拷贝到newDirName，不拷贝文件夹
void WebServer::copyDir(QString newDirName, QString oldDirName)
{
    QDir dir(oldDirName);
    QFile file;
    QStringList listFileName=dir.entryList(QDir::Files);
    while(!listFileName.isEmpty())
    {
        file.setFileName(oldDirName+"/"+listFileName.at(0));
        file.copy(newDirName+"/"+listFileName.at(0));
        listFileName.removeFirst();
    }
}
